using System;
using System.Collections;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
using System.Runtime.Remoting.Channels.Tcp;
using System.Text;
using Biolink.Common.Remoting.Extensions;
using Biolink.Common.Remoting.Sinks;

namespace BioLink.Services
{
    public class GenericServicePublisher
    {
        public static void Publish(ServerLocatorCollection locators)
        {
            TrafficDefaultInfo.Bind();

            StringBuilder info = new StringBuilder();
            info.AppendLine("Open network listeners using:");
            foreach (ServerLocator locator in locators)
                info.AppendLine("  " + locator);

            Trace.WriteLine(info);

            StringBuilder si = new StringBuilder();
            for (int i = 0; i < locators.Count; i++)
            {
                ServerLocator locator = locators[i];
                IServerChannelSinkProvider customServerProvider = null;
                
                Hashtable trafficProviderParameters = new Hashtable();
                trafficProviderParameters["customSinkType"] = typeof(TrafficSink).AssemblyQualifiedName;
                CustomServerSinkProvider trafficProvider = new CustomServerSinkProvider(trafficProviderParameters, new ArrayList());

                IDictionary channelProperties = new Hashtable();
                if (!string.IsNullOrEmpty(locator.PublicName))
                    channelProperties["machineName"] = locator.PublicName;

                else
                {
                    if (locator.IP != IPAddress.Any && locator.IP != null && locator.IP.ToString().Length > 0)
                        channelProperties["bindTo"] = locator.IP.ToString();
                }

                channelProperties["name"] = "Pub_" + (i + 1);
                channelProperties["port"] = locator.Port;
                channelProperties["priority"] = (i + 3);

                channelProperties["exclusiveAddressUse"] = false;

                IDictionary serverFormaterProperties = new Hashtable();
                serverFormaterProperties["includeVersions"] = false;
                BinaryServerFormatterSinkProvider serverProviderBinaryFormatter = new BinaryServerFormatterSinkProvider(serverFormaterProperties, null);
                serverProviderBinaryFormatter.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;

                trafficProvider.Next = serverProviderBinaryFormatter;
                customServerProvider = trafficProvider;

                Hashtable propertiesDelivery = new Hashtable();
                propertiesDelivery["customSinkType"] = typeof (TransparentDeliveryServerSink).AssemblyQualifiedName;
                CustomServerSinkProvider sinkDelivery = new CustomServerSinkProvider(propertiesDelivery, new ArrayList());

                serverProviderBinaryFormatter.Next = sinkDelivery;

                Exception ex = null;
                try
                {
                    TcpServerChannel channel = new TcpServerChannel(channelProperties, customServerProvider);
                    ChannelServices.RegisterChannel(channel, false);
                    ChannelDataStore cds = (ChannelDataStore)channel.ChannelData;
                    foreach (string s in cds.ChannelUris)
                    {
                        si.AppendLine("  " + s);
                    }
                }
                catch(SocketException eee)
                {
                    ex = eee;
                }

                if (ex != null)
                    si.AppendLine("  FAILED publish on " + locator + ": " + ex.Message + Environment.NewLine + ex);
            }

            Trace.WriteLine("Listening on:" + Environment.NewLine + si);
            Subscribe();
        }


        private static TcpClientChannel _TcpClient = null;
        private static IpcClientChannel _IpcClient = null;
        private static readonly object ClientSync = new object();

        public static void Subscribe()
        {
            lock(ClientSync)
                if (_TcpClient == null)
                {
                    IDictionary clientFormaterProperties = new Hashtable();
                    clientFormaterProperties["includeVersions"] = false;
                    BinaryClientFormatterSinkProvider clientProviderBinaryFormatter = new BinaryClientFormatterSinkProvider(clientFormaterProperties, null);

                    CustomClientSinkProvider sp1, sp2, sd;

                    {
                        Hashtable prs = new Hashtable();
                        prs["customSinkType"] = typeof (MyClientSink).AssemblyQualifiedName;
                        sp1 = new CustomClientSinkProvider(prs, new ArrayList());
                    }

                    {
                        Hashtable prs = new Hashtable();
                        prs["customSinkType"] = typeof (MyClientSink).AssemblyQualifiedName;
                        sp2 = new CustomClientSinkProvider(prs, new ArrayList());
                    }

                    {
                        Hashtable prs = new Hashtable();
                        prs["customSinkType"] = typeof (TransparentDeliveryClientSink).AssemblyQualifiedName;
                        sd = new CustomClientSinkProvider(prs, new ArrayList());
                    }

                    sp1.Next = clientProviderBinaryFormatter;
                    clientProviderBinaryFormatter.Next = sp2;
                    sd.Next = sp1;


                    Hashtable pro = new Hashtable();
                    pro["priority"] = 1;
                    pro["name"] = "tcp_client";
                    _TcpClient = new TcpClientChannel(pro, sd);
                    // _TcpClient
                    ChannelServices.RegisterChannel(_TcpClient, false);

                    TransparentDeliveryConfiguration.OnClient +=
                        delegate(MethodBase method, DeliveryDictionary data)
                            {
                                // Trace.WriteLine("Client On Request " + method.DeclaringType + " :: " + method);
                                data.Set("Context", RequestContext.Current);
                            };




                    // IPC Client
                    Hashtable ipcProperties = new Hashtable();
                    ipcProperties["priority"] = 999;
                    ipcProperties["name"] = "ipc_client";
                    IDictionary clientFormaterProperties2 = new Hashtable();
                    clientFormaterProperties2["includeVersions"] = false;
                    BinaryClientFormatterSinkProvider clientProviderBinaryFormatter2 = new BinaryClientFormatterSinkProvider(clientFormaterProperties, null);
                    _IpcClient = new IpcClientChannel(ipcProperties, clientProviderBinaryFormatter2);
                    ChannelServices.RegisterChannel(_IpcClient, true);

                    Trace.WriteLine("Register remoting client channel");


                }
        }

        public static void Clear()
        {
            IChannel[] channels = ChannelServices.RegisteredChannels;
            foreach (IChannel channel in channels)
            {
                if (channel is TcpServerChannel)
                    ChannelServices.UnregisterChannel(channel);
            }
        }
    }
}